import wx
import datetime
import abc
from wx.lib.pubsub import setuparg1
from wx.lib.pubsub import pub


from DataManager import DataManager
from SimpleDialogs import DialogAddItem

import SimplePanels

class ViewBaseList(wx.Panel):
    """
    this is the view part of wish list window
    1. hierarchy:
    list has it's own data structures, this is due to the reason that
    wxpython(wxwidget) do not provide us a very object-oriented framework
    ================================================================================
    model   -->   ViewxxxList                      -->  ui_list
    data          data_list                             the wxwidget
    ListItem      store formated data, so when          component, very unfriendly
    objects       I want to refresh ui_list, I just
                  move things from data_list to
                  ui_list
    ================================================================================
    """
    __metaclass__ = abc.ABCMeta

    @abc.abstractproperty
    def EDIT_DEFAULT_VALUE(self):
        """
        this is used in textctrl to give user a hint
        """
        pass

    @abc.abstractproperty
    def EDIT_MODE(self):
        """
        whether start and end should be activated in add dialog
        """
        pass

    fields = ('title', 'start', 'end', 'type', 'prio')
    widths = (50,150,150,50,50)
    col_cmper = (lambda x, y: cmp(x.title, y.title),
                 lambda x, y: cmp(x.start, y.start),
                 lambda x, y: cmp(x.end, y.end),
                 lambda x, y: cmp(x.type, y.type),
                 lambda x, y: cmp(x.prio, y.prio))

    def __init__(self, parent):
        wx.Panel.__init__(self, parent = parent)
        self.init_widgets()
        self.init_data()
        self.init_control()
        self.update()

    def init_widgets(self):
        l = self.ui_list = wx.ListCtrl(self, style = wx.LC_REPORT)
        for i,j in enumerate(self.fields):
            l.InsertColumn(i, j)
            l.SetColumnWidth(i, self.widths[i])

        e = self.ui_edit = wx.TextCtrl(self,
                                    value=self.EDIT_DEFAULT_VALUE,
                                    style=wx.TE_PROCESS_ENTER)
        self.ui_edit.touched = 0
        b = self.btn_new = wx.Button(self, -1, 'new')
        sizer = wx.GridBagSizer()
        sizer.Add(l, (0,0), flag = wx.EXPAND, span=(1,2))
        sizer.Add(e, (1,0), flag = wx.EXPAND)
        sizer.Add(b, (1,1))
        
        sizer.AddGrowableCol(0)
        sizer.AddGrowableRow(0)

        self.SetSizer(sizer)

    def init_data(self):
        self.order_of_colomn = [False] * len(self.fields)
        self.data_list = list(self.get_model().get())
        self.time_mode = SimplePanels.PANEL_VIEW_TIME_ALL

    def init_control(self):
        self.ui_edit.Bind(wx.EVT_TEXT_ENTER, self.on_edit_enter)
        self.ui_edit.Bind(wx.EVT_SET_FOCUS, self.on_set_focus)
        self.ui_edit.Bind(wx.EVT_KILL_FOCUS, self.on_kill_focus)

        self.ui_list.Bind(wx.EVT_LIST_ITEM_RIGHT_CLICK, self.on_list_right_click)
        self.ui_list.Bind(wx.EVT_LIST_ITEM_ACTIVATED, self.on_list_double_click)

        self.btn_new.Bind(wx.EVT_BUTTON, self.on_edit_enter)

        self.ui_list.Bind(wx.EVT_LIST_COL_CLICK, self.on_list_column_click)
        self.init_subscribes()

    def init_subscribes(self):
        pub.subscribe(self.on_add_new_item, self.get_model_class().M_ADD_ITEM)
        pub.subscribe(self.on_delete_item, self.get_model_class().M_DEL_ITEM)
        #I need to find a way to make
        #change view more local, rather than a broadcast
        pub.subscribe(self.on_change_view_time,
                      self.get_panel_class().M_TIME_CHANGE)
        pub.subscribe(self.on_refresh,
                      self.get_model_class().M_REFRESH_ITEM)


    @abc.abstractmethod
    def get_model_class(self):
        """
        get the class object, not the instance. We need this because the instance
        may not have been created yet
        """
        pass

    @abc.abstractmethod
    def get_model(self):
        """
        subclass must supply super class with this get method
        to get the corresponding model class instance
        """
        pass

    @abc.abstractmethod
    def get_control_class(self):
        """
        subclass must supply super class with this get method
        to get the corresponding control class object
        """
        pass

    @abc.abstractmethod
    def get_panel_class(self):
        """
        we use this to now which panel class we are cooperating with
        """
        pass

    ###############################################################
    # message handlers, used to implement MVC
    ###############################################################
    def on_add_new_item(self, m):
        """
        called when MODEL notify view that a item is added
        NOTE: this is not called when GUI is handling user add events
        this deals with the second phase, when control comes from model back
        to view
        """

        #this is the list maintained by view, not that of model
        #we created a copy of that in init!!!
        self.data_list.append(m.data)
        #NOTE:we can append and call update, that would be easy but too inefficent
        #put item into GUI widget
        p = self.ui_list.Append(self.list_to_display(m.data))
        #In callback, we use this find out which item it is.
        self.ui_list.SetItemData(p, p)

    def list_to_display(self, l):
        return [l.title, l.start, l.end, l.type, l.prio]

    def on_delete_item(self, m):
        """
        called when notified that an item is deleted
        similar to on_add_new_item
        NOTE: to do real deletion is a little bit difficult(bad API),
        so we just delete all and add them back
        """
        #used to make this wrong, should be ui_list
        self.data_list.remove(m.data)
        self.update()

    def on_change_view_time(self, m):
        #clean data_list and load corrosponding data from list
        mode = m.data
        if self.time_mode == mode:
            return

        self.time_mode = mode
        self.data_list = []
        now = datetime.datetime.now()
        for i in self.get_model().get():
            self.time_match(i, now)
            self.data_list.append(i)
        #UI part change, so user sees it
        self.update()

    def time_match(self, t, now):
        tm = t.start
        if self.time_mode == SimplePanels.PANEL_VIEW_TIME_ALL:
            return True
        if tm.year != now.year:
            return False
        if self.time_mode == SimplePanels.PANEL_VIEW_TIME_MONTH:
            if tm.month == now.month:
                return True
            else:
                return False
        if self.time_mode == SimplePanels.PANEL_VIEW_TIME_WEEK:
            if tm.isocalendar()[1] == now.isocalendar()[1]:
                return True
            else:
                return False
        if self.time_mode == SimplePanels.PANEL_VIEW_TIME_DAY:
            if tm.day == now.day:
                return True
            else:
                return False

    def on_refresh(self, m):
        self.data_list = list(self.get_model().get())
        self.update()



    ####################################################################
    # miscellaneous UI stuff
    ####################################################################
    def append(item):
        self.data_list.append(item)

    def on_set_focus(self, evt):
        if self.ui_edit.touched == 0:
            self.ui_edit.SetValue("")

    def on_kill_focus(self, evt):
        txt = self.ui_edit.GetValue()
        if txt == '':
            self.ui_edit.SetValue(self.EDIT_DEFAULT_VALUE)
            self.ui_edit.touched = 0
        else:
            self.ui_edit.touched = 1

    def update(self):
        """
        flush ui_list, synchronize ui_list and data_list
        """
        self.ui_list.DeleteAllItems()
        for i in self.data_list:
            p = self.ui_list.Append(self.list_to_display(i))
            self.ui_list.SetItemData(p, p)

    ####################################################################
    # control stubs
    ####################################################################

    def on_edit_enter(self, evt):
        """
        handle the case when enter is pressed in TxtCtrl
        """
        self.get_control_class().control_edit_enter(self, evt, self.EDIT_MODE)

    def on_list_right_click(self, evt):
        self.get_control_class().control_list_right_click(self, evt)


    def on_list_double_click(self, evt):
        self.get_control_class().control_list_double_click(self, evt)


    def on_list_column_click(self, evt):
        self.get_control_class().control_list_column_click(self, evt)
